from expression import expression, environment
import sys
sys.path.append(sys.path[0] + "/../LLVM")
from llvmhelper import llvmHelper
from llvm import *
from commonRoutines import commonRoutines


class functionDef(expression):
	def __init__(self, name, params, body):
		self.name = name
		self.params = params
		self.body = body
		self.env = environment("", expression())
		self.eframe = None
		self.FunNum = None

	def evaluate(self, env):
		self.env = env
		self.body.evaluate(env)
		
	def getName(self):
		return self.name
		
	def getParams(self):
		return self.params
	
	def getBody(self):
		return self.body
	
	def getEnv(self):
		return self.env
		
	def getFunNum(self):
		return self.FunNum
		
	def __str__(self):
		value = "functionDef(" + str(self.name) +"["
		for p in self.params:
			value += str(p) + ","
		value += "]"
		value += "\n" + str(self.body) + "\n)"
		return value

	def traverse(self, helper):
		#print "Traversing " + self.name
		#adds the function def to the list
		helper.addFunctionDef(self.name)
		self.FunNum = helper.getFunctionDef(self.name)
		helper.addVarToScope(self.name)
		for a in self.params:
			if(a != None):
				helper.addArgToFunction(str(a))
		#helper.displayScope()
		scope = helper.getCurrentScope()
		var = scope.get(self.name)
		#print var + " name: " + self.name
		self.eframe = var
		if(str(self.body)[1] == "S" and str(self.body)[2] == "c"):
			self.body.setFunctionScope()
		#self.body.traverse(helper)
		
	def toLLVM(self, helper):
	#%closure = type{i32,i32, %eframe*}\n"
		helper.descend("funDef")
		
		c = commonRoutines(helper)

		mallocReg = helper.getNextReg()
		bitcastReg = helper.getNextReg()
		closureTypeAddrReg = helper.getNextReg()
		closureNumberAddrReg = helper.getNextReg()
		secondBitcastReg = helper.getNextReg()
		pointerAsIntReg = helper.getNextReg()
		addrofEframeInClosureReg = helper.getNextReg()
		
	
		helper.addLLVMInst(llvm_malloc(mallocReg, "%closure"), "Creating closure")
		helper.addLLVMInst(llvm_gep(closureTypeAddrReg, mallocReg, "%closure*", 0, "i32", 0), "getting pointer to type reg")
		helper.addLLVMInst(llvm_store(str(1), closureTypeAddrReg, "i32", "i32"), "saving type")
		
		helper.addLLVMInst(llvm_gep(closureNumberAddrReg, mallocReg, "%closure*", 0, "i32", 1), "getting pointer number")
		helper.addLLVMInst(llvm_store(str(helper.getFunctionIndex(self.name)), closureNumberAddrReg, "i32", "i32"), "saving function ID")		
		
		currentScopeReg =  helper.getCurrentScopeReg()
		
		helper.addLLVMInst(llvm_sub(pointerAsIntReg, currentScopeReg, "1"), "removing tag")
		helper.addLLVMInst(llvm_inttoptr(secondBitcastReg, pointerAsIntReg, "i32", "i32*"), "turning int into pointer")
		helper.addLLVMInst(llvm_bitcast(bitcastReg, secondBitcastReg, "i32*", "%eframe*"), "Bitcasting to %eframe*")
		helper.addLLVMInst(llvm_gep(addrofEframeInClosureReg, mallocReg, "%closure*", 0, "i32", 2), "getting addr of Eframe")
		helper.addLLVMInst(llvm_store(bitcastReg, addrofEframeInClosureReg, "%eframe*", "%eframe*"), "saving previous scope")
		
		closureAsIntReg = c.pointerToInt(mallocReg, "%closure")
		

		
		#sys.stderr.write("self.eframe: " + str(self.eframe))
		c.setVar(self.eframe[1], self.eframe[2], closureAsIntReg)
		
		helper.enterFunction(self.name)
		
		self.body.toLLVM(helper)
		
		helper.setLabel(helper.getLastLabel())
		helper.ascend(helper.getReturns()[0])
		helper.exitFunction()
